home *** CD-ROM | disk | FTP | other *** search
-
- /*
- * (a) (C) 1990 by Adobe Systems Incorporated. All rights reserved.
- *
- * (b) If this Sample Code is distributed as part of the Display PostScript
- * System Software Development Kit from Adobe Systems Incorporated,
- * then this copy is designated as Development Software and its use is
- * subject to the terms of the License Agreement attached to such Kit.
- *
- * (c) If this Sample Code is distributed independently, then the following
- * terms apply:
- *
- * (d) This file may be freely copied and redistributed as long as:
- * 1) Parts (a), (d), (e) and (f) continue to be included in the file,
- * 2) If the file has been modified in any way, a notice of such
- * modification is conspicuously indicated.
- *
- * (e) PostScript, Display PostScript, and Adobe are registered trademarks of
- * Adobe Systems Incorporated.
- *
- * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
- * CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
- * AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
- * ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
- * OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
- * WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
- * WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
- * DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
- * OF THIRD PARTY RIGHTS.
- */
-
- /*
- * Bezier.m
- *
- * The methods here handle the actions necessary to create and draw
- * a bezier curve. This is the only graphical object in the application.
- * Some methods found here would probably be moved to a generic
- * graphical object class in a more general application. Some methods
- * have special steps included because of the interface decisions
- * chosen for a bezier curve. When the first and last control points
- * are moved, the second and third control points, respectively, are
- * also moved. This has implications when scrolling and restraining the
- * drawing to the imagable area.
- *
- * Version: 2.0
- * Author: Ken Fromm
- * History:
- * 03-07-91 Added this comment.
- */
-
- #import "Bezier.h"
- #import "DetectApp.h"
- #import "DocView.h"
- #import "DrawingView.h"
- #import "DrawingViewWraps.h"
- #import <appkit/nextstd.h>
- #import <dpsclient/dpsclient.h>
- #import <dpsclient/wraps.h>
-
- @implementation Bezier
-
- - initFrame:(NXRect *)frm
- {
- self = [super init];
-
- NX_MALLOC(path.pts, float, PTS_BEZIER*2 + 4);
- NX_MALLOC(path.ops, char, 3);
-
- [self create:frm];
-
- return self;
- }
-
- /* Randomly select the points and calculate the bounds. */
- - create:(const NXRect *)frm
- {
- int i;
-
- LLX(path.pts) = LLY(path.pts) = 9999;
- URX(path.pts) = URY(path.pts) = -9999;
-
- for (i = 0; i < PTS_BEZIER * 2; i += 2)
- {
- path.pts[i + 4] = rand () % ((int)frm->size.width -8) + frm->origin.x + 4;
- path.pts[i + 5] = rand () % ((int)frm->size.height -8) + frm ->origin.y + 4;
-
- LLX(path.pts) = MIN(LLX(path.pts), path.pts[i + 4]);
- LLY(path.pts) = MIN(LLY(path.pts), path.pts[i + 5]);
- URX(path.pts) = MAX(URX(path.pts), path.pts[i + 4]);
- URY(path.pts) = MAX(URY(path.pts), path.pts[i + 5]);
- }
- path.num_pts = PTS_BEZIER * 2 + 4;
-
- /*
- * The first entry is set to dps_setbbox for hit detection but then reset
- * to dps_ucache when drawing (except when the user is changing
- * the shape of the object frequently.)
- */
- path.ops[0] = dps_setbbox;
- path.ops[1] = dps_moveto;
- path.ops[2] = dps_curveto;
- path.num_ops = 3;
-
- return self;
- }
-
- - (UPath *) getPath
- {
- return &path;
- }
-
- - copyPts:srcId
- {
- UPath *srcPath;
-
- srcPath = [srcId getPath];
-
- NX_MALLOC(path.pts, float, PTS_BEZIER*2 + 4);
- NX_MALLOC(path.ops, char, 3);
-
- bcopy(srcPath->pts, path.pts, srcPath->num_pts* (sizeof(float)/sizeof(char)));
- bcopy(srcPath->ops, path.ops, srcPath->num_ops);
-
- return self;
- }
-
- /*
- * Returns the bounds. The flag variable determines whether the
- * knobs should be factored in. They may need to be for drawing but
- * might not if needed for constraining reasons.
- */
- - getBounds:(NXRect *)bRect withKnobs:(BOOL) flag
- {
- float knobsize;
-
- bRect->origin.x = LLX(path.pts);
- bRect->origin.y = LLY(path.pts);
- bRect->size.width = URX(path.pts) - LLX(path.pts);
- bRect->size.height = URY(path.pts) -LLY(path.pts);
-
- if (flag)
- {
- knobsize = -[[[NXApp getDrawingView] superview] controlPointSize]/2;
- NXInsetRect(bRect, knobsize, knobsize);
- NXIntegralRect(bRect);
- }
-
- return self;
- }
-
- /* Given the point number, return the point. */
- - getPoint:(int) pt_num :(NXPoint *) pt
- {
- pt->x = path.pts[pt_num*2 + 4];
- pt->y = path.pts[pt_num*2 + 5];
-
- return self;
- }
-
- /*
- * Depending on the pt_num passed in, return the rectangle
- * that should be used for scrolling purposes. When the rectangle
- * passes out of the visible rectangle then the screen should
- * scroll. If the first and last points are selected, then the second
- * and third points are included in the rectangle. If the second and
- * third points are selected, then they are used by themselves.
- */
- - getScrollRect:(int) pt_num :(NXRect *) aRect
- {
- float knobsize;
-
- if (pt_num == -1)
- {
- [self getBounds:aRect withKnobs:NO];
- }
- else if (pt_num == 0)
- {
- aRect->origin.x = MIN(path.pts[4], path.pts[6]);
- aRect->origin.y = MIN(path.pts[5], path.pts[7]);
- aRect->size.width = ABS(path.pts[4] - path.pts[6]);
- aRect->size.height = ABS(path.pts[5] - path.pts[7]);
- }
- else if (pt_num == 3)
- {
- aRect->origin.x = MIN(path.pts[10], path.pts[8]);
- aRect->origin.y = MIN(path.pts[11], path.pts[9]);
- aRect->size.width = ABS(path.pts[10] - path.pts[8]);
- aRect->size.height = ABS(path.pts[11] - path.pts[9]);
- }
- else
- {
- aRect->origin.x = path.pts[pt_num*2 + 4];
- aRect->origin.y = path.pts[pt_num*2 + 5];
- aRect->size.width = 0;
- aRect->size.height = 0;
- }
-
- knobsize = -[[[NXApp getDrawingView] superview] controlPointSize]/2;
- NXInsetRect(aRect, knobsize, knobsize);
-
- return self;
- }
-
- /*
- * This method constains the point to the bounds of the view passed
- * in. Like the method above, the constaining is dependent on the
- * control point that has been selected.
- */
- - constrainPoint:(NXPoint *)aPt andNumber:(int) pt_num toView:aView
- {
- float knobsize;
-
- NXPoint viewMax;
-
- NXPoint *thisPt, *nextPt;
-
- NXRect viewRect;
-
- [aView getBounds:&viewRect];
- viewMax.x = viewRect.origin.x + viewRect.size.width;
- viewMax.y = viewRect.origin.y + viewRect.size.height;
-
- if (pt_num == 0 || pt_num == 3)
- {
- thisPt = (NXPoint *) &path.pts[pt_num*2 + 4];
- if (pt_num == 0)
- nextPt = (NXPoint *) &path.pts[6];
- else
- nextPt = (NXPoint *) &path.pts[8];
-
- if (thisPt->x > nextPt->x)
- viewRect.origin.x += thisPt->x - nextPt->x;
- else
- viewMax.x -= nextPt->x - thisPt->x;
-
- if (thisPt->y > nextPt->y)
- viewRect.origin.y += thisPt->y - nextPt->y;
- else
- viewMax.y -= nextPt->y - thisPt->y;
- }
-
- viewMax.x -= MARGIN;
- viewMax.y -= MARGIN;
- viewRect.origin.x += MARGIN;
- viewRect.origin.y += MARGIN;
-
- aPt->x = MAX(viewRect.origin.x, aPt->x);
- aPt->y = MAX(viewRect.origin.y, aPt->y);
-
- aPt->x = MIN(viewMax.x, aPt->x);
- aPt->y = MIN(viewMax.y, aPt->y);
-
- return self;
- }
-
- /*
- * Change the point number passed in by the amount passed in in pt.
- * Recalculate the bounds because one of the bounding points could
- * have been the changed point.
- */
- - changePoint:(int) pt_num :(const NXPoint *) pt
- {
- int i;
-
- path.pts[pt_num *2 + 4] += pt->x;
- path.pts[pt_num *2 + 5] += pt->y;
-
- LLX(path.pts) = LLY(path.pts) = 9999;
- URX(path.pts) = URY(path.pts) = -9999;
- for (i = 0; i < PTS_BEZIER * 2; i += 2)
- {
- LLX(path.pts) = MIN(LLX(path.pts), path.pts[i + 4]);
- LLY(path.pts) = MIN(LLY(path.pts), path.pts[i + 5]);
- URX(path.pts) = MAX(URX(path.pts), path.pts[i + 4]);
- URY(path.pts) = MAX(URY(path.pts), path.pts[i + 5]);
- }
-
- return self;
- }
-
- /*
- * pt_num is the changing control point. pt holds the relative change in each coordinate.
- * The relative is needed and not the absolute because the closest inside control point
- * changes when one of the outside points change.
- */
- - setPoint:(int) pt_num :(const NXPoint *) pt
- {
- [self changePoint:pt_num :pt];
-
- if (pt_num == 0)
- [self changePoint:1 :pt];
- else if (pt_num == 3)
- [self changePoint:2 :pt];
-
- return self;
- }
-
- /* The pt argument holds the relative point change. */
- - moveAll:(const NXPoint *) pt
- {
- int i;
-
- for (i = 0; i < PTS_BEZIER * 2; i += 2)
- {
- path.pts[i + 4] +=pt->x;
- path.pts[i + 5] += pt->y;
- }
-
- LLX(path.pts) += pt->x;
- LLY(path.pts) += pt->y;
- URX(path.pts) += pt->x;
- URY(path.pts) += pt->y;
-
- return self;
- }
-
- /*
- * Check for a control point hit. No need to perform the hit detection in
- * the server since its a simple rectangle intersection check. Return the
- * point number hit in the pt_num argument.
- */
- - (BOOL) hitControl:(const NXRect *)hitRect :(int *) pt_num :(float) controlsize
- {
- int i;
-
- NXRect knobRect;
-
- knobRect.size.width = knobRect.size.height = controlsize;
- for (i=0; i < PTS_BEZIER*2; i += 2)
- {
- knobRect.origin.x = path.pts[i + 4] - controlsize/2;
- knobRect.origin.y = path.pts[i + 5] - controlsize/2;
- if (NXIntersectsRect(hitRect, &knobRect))
- {
- *pt_num = i/2;
- return YES;
- }
- }
-
- return NO;
- }
-
- /*
- * Check for hit dectection on the object. This uses the
- * inustroke operator to check for an intersection of the
- * hit detection rectangle with the path of the Bezier.
- */
- - (BOOL) hitObject:(UPath *) hitUpath
- {
- int hit = NO;
-
- NXRect aRect, bRect;
-
- NXSetRect(&aRect, hitUpath->pts[0], hitUpath->pts[1],
- hitUpath->pts[2] - hitUpath->pts[0],
- hitUpath->pts[3] - hitUpath->pts[1]);
- [self getBounds:&bRect withKnobs:NO];
- if (NXIntersectsRect(&aRect, &bRect))
- {
- path.ops[0] = dps_setbbox;
- PSWHitPath(hitUpath->pts, hitUpath->num_pts, hitUpath->ops, hitUpath->num_ops,
- path.pts, path.num_pts, path.ops, path.num_ops, &hit);
- }
-
- return (BOOL) hit;
- }
-
- /*
- * Place the point locations and the chararacters for the control
- * points into the user path description passed in. In this case, the
- * xyshow operator is used instead of a user path. But because the
- * xyshow operator takes the same data format as the user path,
- * the buffers for the user paths are used. The position of the point
- * is calculated relative to the position of the previous point. If
- * this is the first set of points in the description then put the
- * absolute values in place, otherwise use the lastPoint
- * argument to calculate the displacement.
- */
- - putControlUPath:(UPath *)drawUpath forRect:(NXRect *)r :(NXPoint *) lastPoint
- {
- int i, j;
-
- NXRect bounds;
-
- [self getBounds:&bounds withKnobs:YES];
- if (!r || NXIntersectsRect(r, &bounds))
- {
- i = drawUpath->num_ops;
- drawUpath->ops[i++] = 'a';
- drawUpath->ops[i++] = 'b';
- drawUpath->ops[i++] = 'b';
- drawUpath->ops[i++] = 'a';
- drawUpath->num_ops += PTS_BEZIER;
-
- i = drawUpath->num_pts;
- if (i == 0)
- {
- drawUpath->pts[i++] = path.pts[4];
- drawUpath->pts[i++] = path.pts[5];
- }
- else
- {
- drawUpath->pts[i++] = path.pts[4] - lastPoint->x;
- drawUpath->pts[i++] = path.pts[5] - lastPoint->y;
- }
-
- for (j = 2; i < PTS_BEZIER * 2; j++, i++)
- drawUpath->pts[i] = path.pts[j + 4] - path.pts[j + 2];
-
- drawUpath->num_pts += PTS_BEZIER*2;
- lastPoint->x = path.pts[10];
- lastPoint->y = path.pts[11];
- }
-
- return self;
- }
-
- /*
- * Place the description of the control lines into the the user path.
- * Update the bounding box of the user path if necessary.
- */
- - putControlLinesUPath:(UPath *) drawUpath forRect:(NXRect *) r
- {
- int i;
-
- NXRect bounds;
-
- [self getBounds:&bounds withKnobs:YES];
- if (!r || NXIntersectsRect(r, &bounds))
- {
- i = drawUpath->num_ops;
- drawUpath->ops[ i++] = dps_moveto;
- drawUpath->ops[ i++] = dps_lineto;
- drawUpath->ops[ i++] = dps_moveto;
- drawUpath->ops[ i++] = dps_lineto;
- drawUpath->num_ops += 4;
-
- for (i=0; i < PTS_BEZIER * 2; i++)
- drawUpath->pts[drawUpath->num_pts + i] = path.pts[i + 4];
- drawUpath->num_pts += PTS_BEZIER*2;
-
- drawUpath->pts[0] = MIN(LLX(path.pts), drawUpath->pts[0]);
- drawUpath->pts[1] = MIN(LLY(path.pts), drawUpath->pts[1]);
- drawUpath->pts[2] = MAX(URX(path.pts), drawUpath->pts[2]);
- drawUpath->pts[3] = MAX(URY(path.pts), drawUpath->pts[3]);
- }
-
- return self;
- }
-
- /*
- * Draws the graphic if it lies within the bounds of the rectangle passed in.
- * Draws with the ucache on if uFlag is YES.
- */
- - drawObject:(NXRect *)r withUcache:(BOOL)uFlag
- {
- int start_pt = 1;
-
- NXRect bounds;
-
- [self getBounds:&bounds withKnobs:NO];
- if (!r || NXIntersectsRect(r, &bounds))
- {
- PSsetgray(COLOR);
- PSsetlinewidth(WIDTH);
- if (uFlag)
- {
- path.ops[0] = dps_ucache;
- start_pt = 0;
- }
- DPSDoUserPath(&path.pts[4], path.num_pts-4, dps_float,
- &path.ops[start_pt], path.num_ops - start_pt, path.pts, dps_ustroke);
- }
-
- return self;
- }
-
- @end
-